open Ast
open Mips

let maxMemory = ref 0

(* Information sur une variable locale ou qui fait partie d'une structure *)
type decl_type_info = {
	decl_info_size : int;
	mutable decl_info_offset : int;
} 

(* Information sur une variable de type structure ou union *)
type decl_struct_info = {
	decl_info : decl_type_info;
	decl_champs : (string * decl_type_info) list;
}

(* Information sur une fonction *)
type decl_fun_info = {
	taille_retour : int;
	decl_args : (string * decl_type_info) list;
}

(* Recuperation d'un numero unique pour les flag while, if .... et les string *)
let compteur = ref 0
let num_unique () =
	compteur := !compteur + 1; (!compteur - 1)

(* Information sur les variables globales *)
let (decl_type_env_var : ((string, decl_type_info) Hashtbl.t)) = Hashtbl.create 0 

(* On va enregistre la taille des struct et autre information *)
let (decl_type_env_struct : ((string, decl_struct_info) Hashtbl.t)) = Hashtbl.create 0 

(* Information sur les fonction *)
let (decl_type_env_fun : ((string, decl_fun_info) Hashtbl.t)) = Hashtbl.create 0

let strings = ref []

(* On calcule le offset de chaque element d'une structure 
 * @author : Oliver De Cramer 
 *)
let rec compile_compute_info_struct l  = 
	(* Le offset de l'element *)
	let offset = ref 0 in

	(* Pour chaque element *)
	let rec loop l = 
		match l with
			| (t, s)::l ->
				let s = s.node in
				( match t with
					(* Void a taille 0 on continue *)
					| Tvoid -> (loop l)
					| Tnull | Tint | Tpointer _ | Tchar _-> 
 							(* On calcul le offset *)
 							offset := !offset + 4;
 							(* L'element avec son nom, ça taille et son offset *)
							let ret = (s, {decl_info_size = 4; decl_info_offset=(!offset-4)}) in
							(ret ::(loop l))
					| Tstruct id  | Tunion id	->
							(* On recupere les information sur la strucutr/union *)
							let info = Hashtbl.find decl_type_env_struct id.node in
							(* On calcul le offset *)
							offset := !offset + info.decl_info.decl_info_size;
							let ret = (s, {decl_info_size = info.decl_info.decl_info_size; decl_info_offset=(!offset-info.decl_info.decl_info_size)}) in
							(ret::(loop l))
				)
			| [] -> []
	in
	(* On boucle sur chaque element de la liste *)
	let info = loop l in
	{
		decl_info = 
			{
				decl_info_size = !offset;
				decl_info_offset = 0;
			};
		decl_champs = info;
	}
(* On calcule le offset de chaque element d'une union 
 * @author : Oliver De Cramer
 *)
and compile_compute_info_union l = 
	let max = ref 0 in

	let rec loop l = 
		match l with
			| (t, s)::l ->
				let s = s.node in
				( match t with
					| Tvoid -> (loop l)
					| Tnull | Tint | Tpointer _-> 
							((s, {decl_info_size = 4; decl_info_offset=0})::(loop l))
					| Tchar -> 				
							((s, {decl_info_size = 1; decl_info_offset=0})::(loop l))	
					| Tstruct id  | Tunion id ->
							let info = Hashtbl.find decl_type_env_struct id.node in
							((s, {decl_info_size = info.decl_info.decl_info_size; decl_info_offset=0})::(loop l))
				)
			| [] -> []
	in
	let info = loop l in
	{
		decl_info = 
			{
				decl_info_size = !max;
				decl_info_offset = 0;
			};
		decl_champs = info;
	}

(* Calcul la taille d'un ctype 
 * @author : Julien Christophe
 *)
let compile_compute_type t =
	match t with 
	  | Tnull -> 4
	  | Tvoid -> 0
	  | Tint -> 4
	  | Tchar -> 4
	  | Tstruct id | Tunion id -> Printf.printf "%s\n" id.node;
			let st = (Hashtbl.find decl_type_env_struct id.node).decl_info.decl_info_size in 
			st
	  | Tpointer _ -> 4

(* Genere le code necessaire pour les variable globale 
 * @author : Julien Christophe
 *)
let rec compile_global_vars vars =  
	match vars with
		| [] -> []
		| (ctyp,id)::ss ->
			match ctyp with
				| Tnull | Tvoid | Tint | Tchar | Tpointer _ -> 
					[
						(Mips.Dlabel id.node);
						(Mips.Dword [Int32.zero])
					]
					@compile_global_vars ss
					
				| Tstruct s | Tunion s ->
					let size =  (Hashtbl.find decl_type_env_struct s.node).decl_info.decl_info_size in
					[
						(Mips.Dlabel id.node);
						(Mips.Dspace size)
					]
					@compile_global_vars ss

(* Genere le code necessaire pour enregistrer les registre important a l'appel d'une fonction
 * @author : Julien Christophe
 *)
let enregistrer_ra_fp () =
	[
		Comment ("sauvegarde de FP et de RA");
		Sw(FP,Areg(0,SP));
		Sw(RA,Areg(-4,SP));
		Binop(Add,SP,SP,Oimm(-8));
		Binop(Add,FP,SP,Oimm(0))
	]

(* Genere le code necessaire pour restituer les registre important a l'appel d'une fonction 
 * @author : Julien Christophe
 *)
let recuperer_ra_fp () =
	[
		Comment ("recuperation de FP et de RA");
		Lw(RA,Areg(4,FP));
		Binop(Add,SP,FP,Oimm(8));
		Lw(FP,Areg(8,FP));
	]

(* Genere le code necessaire pour deplacer les information d'une taille n d'un bout de la memoire a un autre. 
 * Cette fonction on realite effectue une copie puisque elle desaloue pas la memoire.
 * @author : Oliver De Cramer
 *)
let rec deplacement n reg_adresse1 reg_adresse2 =
	match n with
		| 0 -> []
		(*| i with (i mod 4 != 0) -> [Lb(T0, Areg(n-4, reg_adresse1));Sb(T0,Are(n-4,reg_adresse2))]@(deplacement (n-1) reg_adresse1 reg_adresse2)*)
			(** tjrs aligné **)
		| _ -> [Lw(A3, Areg(-(n-4), reg_adresse1));Sw(A3,Areg(-(n-4),reg_adresse2))]@(deplacement (n-4) reg_adresse1 reg_adresse2)

(* Compile une liste d'expression et desaloue la memoire utilise (POP dans la pile)
 * @author : Oliver De Cramer
 *)
let rec compile_expr_list expr_list arg_list env =
	match expr_list, arg_list with
	| ([], []) -> []
	| (a::s, d::l) -> let taille = compile_compute_type a.loc in
			(compile_expr a env)
			@(compile_expr_list s l env)
	| _, _ -> assert false

(* Compile les expression. 
 *	-Elle peut calculer la valeur ou une adresse memoire. 
 *	-Elle peut enregistre les resultat au dessus de la pile ou dans un registre
 *
 * @author : Oliver De Cramer & Julien Christophe
 *)
and compile_expr ?registre ?calcul_adresse expr env =
	match expr.node with
  | Enull -> 
  	(* @author Julien Christophe *)
  	(match registre with
		| Some(x) -> [Move(x,ZERO)]
		| None ->  [Sw(ZERO, Areg(0,SP)); Binop(Add,SP,SP,Oimm(-4))]
	)

  | Econst constant -> (
		match constant with
			| Cint i -> 
				(* @author : Oliver De Cramer *)
					(
						match registre with
							| Some(x) -> [Li32(x,i)]
							| None -> [Li32(A0, i); Sw(A0, Areg(0,SP)); Binop(Add,SP,SP,Oimm(-4))]
					)
			| Cstring s -> 
				(* @author : Julien Christohe *)
				let n = num_unique () in
				let flag_string = "string_"^(string_of_int n) in
				let rec string_modifiee s =
					match s with
					| "" -> ""
					| s -> let a =
						match (String.sub s 0 1) with 
						| "\n" -> "\\n"
						| "\t" -> "\\t"
						| "\b" -> "\\b"
						| "\r" -> "\\r"
						| "\\" -> "\\\\"
						| "\'" -> "\\\'"
						| "\"" -> "\\\""
						| "\000" -> "\\0"
						| a -> a
					in let s = (String.sub s 1 ((String.length s) - 1)) in (a^"\\0\\0\\0"^string_modifiee s)
				in
				strings := (flag_string,string_modifiee s):: !strings;
							(
								match registre with
									| Some(x) -> [La(x,flag_string)]
									| None -> [La(A0,flag_string); Sw(A0, Areg(0,SP)); Binop(Add,SP,SP,Oimm(-4))]
							)
		
	)

  | Eident ident -> 
  	(* author : Oliver De Cramer *)
  	(match calcul_adresse with
		(** valeur Ident **)
		| None -> 
			(try (
				let var = Hashtbl.find env ident.node in
				[Inline "\n"; Comment ("Chargement de " ^ ident.node)]
				@(match registre with
					| None -> 
						[	Binop(Add,SP,SP, Oimm(-compile_compute_type expr.loc));
							Binop(Add,T0, FP, Oimm(var.decl_info_offset));
							Binop(Add,V0, SP, Oimm(compile_compute_type expr.loc))
						]
						@deplacement (compile_compute_type expr.loc) T0 V0

					| Some(x) -> [Lw(x, Areg(var.decl_info_offset,FP))]
					
				)@[Inline "\n"]
			) with Not_found -> ((** alors c'est une variable globale **)
				[Inline "\n"; Comment ("Chargement de " ^ ident.node)]@
				(match registre with
					| None -> 
						[	Binop(Add,SP,SP, Oimm(-compile_compute_type expr.loc));
							La(T0,ident.node);
							Binop(Add,V0, SP, Oimm(compile_compute_type expr.loc))
						]
						@deplacement (compile_compute_type expr.loc) T0 V0

					| Some(x) -> [La(A0, ident.node); Lw(x,Areg(0,A0))]

				)@[Inline "\n"]
			)
		)
			
		(** adresse Ident **)
		| Some(_) -> 
			(try (
				let var = Hashtbl.find env ident.node in
				[Inline "\n"; Comment ("Chargement de l'adresse de " ^ ident.node)]@
				(match registre with
					| None -> [
									Binop(Add,A0,FP,Oimm(var.decl_info_offset));
									Sw(A0, (Areg(0,SP))); 
									Binop(Add,SP,SP,Oimm(-4))
								]
					| Some(x) -> 
						[Binop(Add,x,FP,Oimm(var.decl_info_offset))]
				)@[Inline "\n"]
			)
			with Not_found -> ((** alors c'est une variable globale **)
				[Inline "\n"; Comment ("Chargement de l'adresse de " ^ ident.node)]@
				(match registre with
					| None -> [
									La(A0,ident.node);
									Sw(A0, (Areg(0,SP))); 
									Binop(Add,SP,SP,Oimm(-4))
								]
					| Some(x) -> 
						[La(x,ident.node)]
				)@[Inline "\n"]
			)
		)
	)

  | Esizeof c_type -> 
  	(* author : Oliver De Cramer *)
  	(
  		match registre with
			| Some(x) -> [Binop(Add,x,ZERO,Oimm(compile_compute_type c_type))]
			| None -> 
				[
					
					Li(A0, compile_compute_type c_type); 
					Sw(A0, (Areg(0,SP)));
					Binop(Add,SP,SP,Oimm(-4))
				]
	)
  | Edot (expr1,ident) -> 
  	(* author : Oliver De Cramer *)
		(** expr : adresse **)
		let nom_structure = (match expr1.loc with
			| Tstruct id | Tunion id -> id.node
			| _ -> assert false
		) in
		let struc = Hashtbl.find decl_type_env_struct nom_structure in
		
		let rec decalage l =
			match l with
			| [] -> assert false
			| (n, decl_type_info)::s -> Printf.printf "name : %s , %n\n" n decl_type_info.decl_info_offset; if String.compare n ident.node == 0 then decl_type_info else decalage s
		in
		let champ = decalage struc.decl_champs in
		let e = compile_expr ~registre:V0 ~calcul_adresse:true expr1 env in 
		e @
		
		(
			match calcul_adresse with
				| None -> 
					(
						match registre with
							| Some r -> [Lw(r,Areg(-champ.decl_info_offset,V0))]
							| None -> 
								[
									Binop(Add,A0,V0,Oimm(-champ.decl_info_offset)); 
									Binop(Add,A1,SP,Oimm(0)); 
									Binop(Add,SP,SP,Oimm(-champ.decl_info_size))
								]@(deplacement champ.decl_info_size A0 A1)
					)
				| Some _ -> 
					(
						match registre with
							| Some r -> [Binop(Add,r,V0,Oimm(-champ.decl_info_offset))]
							| None -> [Binop(Add,A0,V0,Oimm(-champ.decl_info_offset)); Sw(A0,Areg(0,SP)); Binop(Add,SP,SP,Oimm(-4))]
				
					)
		)


  | Eassign (expr1, expr2) -> 
  	(* author : Julien Christophe *)
  	(
		match registre with
		| None -> (
			let taille = compile_compute_type expr2.loc in
			Printf.printf "taille : %n\n" taille;
			[Comment ("Debut assignation "); Inline "\n"]
			(** resultat en haut de la pile **)
			@(compile_expr expr2 env) 
		
			(** inversion du traitement des 2 expressions pour optimisation **)
			@(compile_expr ~registre:V0 ~calcul_adresse:true expr1 env)
			@[Binop(Add,T0,SP,Oimm(taille))]
		
			(** deplacement du resultat ou il faut **)
			@deplacement taille T0 V0 
		
			(** Faut garder le resultat dans la pile. En desalout pas**)		
			@[Comment ("Fin assignation " ); Inline "\n"]
		)
		| Some x -> (
			let taille = compile_compute_type expr2.loc in
		
			[Comment ("Debut assignation "); Inline "\n"]
			(** resultat en haut de la pile **)
			@(compile_expr expr2 env) 
		
			(** inversion du traitement des 2 expressions pour optimisation **)
			@(compile_expr ~registre:V0 ~calcul_adresse:true expr1 env)
			@[Binop(Add,T0,SP,Oimm(taille))]
		
			(** deplacement du resultat ou il faut **)
			@deplacement taille T0 V0 
			@[Binop(Add,SP,SP,Oimm(taille)); Move(x,A3)]
			(** Faut garder le resultat dans la pile. En desalout pas**)		
			@[Comment ("Fin assignation " ); Inline "\n"]
		)
	)

  | Eunop (unop, expr1) -> 
  	(match unop with
		| Upre_inc ->
			(* author : Oliver De Cramer *)
			let incr = 
				match expr.loc with
					| Tpointer t -> (compile_compute_type t)
					| _ -> 1
			in
			
			(compile_expr ~registre:A0 ~calcul_adresse:true expr1 env)
			@
			[
				Lw(A1, Areg(0, A0));
				Binop(Add, A1, A1, Oimm(incr));
				Sw(A1, Areg(0, A0));
			]@(
					match registre with
						| Some(x) -> [Binop(Add, x, A1, Oimm(0))];
						| None -> [Sw(A1,Areg(0,SP));Binop(Add,SP,SP,Oimm(-4))]
				)
			
					
		| Upost_inc -> 
			(* @author : Oliver De Cramer *)
			let incr = 
				match expr.loc with
					| Tpointer t -> (compile_compute_type t)
					| _ -> 1
			in
			(compile_expr ~registre:A0 ~calcul_adresse:true expr1 env)
			@
			[
				Lw(A1, Areg(0, A0));
				Binop(Add, A2, A1, Oimm(incr));
				Sw(A2, Areg(0, A0));
			]@(
					match registre with
						| Some(x) -> [Binop(Add, x, A1, Oimm(0))];
						| None -> [Sw(A1,Areg(0,SP));Binop(Add,SP,SP,Oimm(-4))]
				)
		
		
		| Upre_dec ->
			(* @author : Oliver De Cramer *)
			let incr = 
				match expr.loc with
					| (Tpointer t) ->
						(compile_compute_type t)
					| _ -> 1
			in
			
			(compile_expr ~registre:A0 ~calcul_adresse:true expr1 env)
			@
			[
				Lw(A1, Areg(0, A0));
				Binop(Sub, A1, A1, Oimm(incr));
				Sw(A1, Areg(0, A0));
			]@(
					match registre with
						| Some(x) -> [Binop(Add, x, A1, Oimm(0))];
						| None -> [Sw(A1,Areg(0,SP));Binop(Add,SP,SP,Oimm(-4))]
				)
		
		| Upost_dec ->
			(* @author : Oliver De Cramer *)
			let incr = 
				match expr.loc with
					| (Tpointer t) ->
						(compile_compute_type t)
					| _ -> 1
			in
			
			(compile_expr ~registre:A0 ~calcul_adresse:true expr1 env)
			@
			[
				Lw(A1, Areg(0, A0));
				Binop(Sub, A2, A1, Oimm(incr));
				Sw(A2, Areg(0, A0));
			]@(
					match registre with
						| Some(x) -> [Binop(Add, x, A1, Oimm(0))];
						| None -> [Sw(A1,Areg(0,SP));Binop(Add,SP,SP,Oimm(-4))]
				)
		
		| Ustar -> 	
			(* @author : Oliver De Cramer *)
			(match calcul_adresse with
				| None -> 
					(compile_expr ~registre:A0 expr1 env) (** A0 contient l'addresse **)
					@(
						match registre with
							| Some(x) -> [Lw(x, Areg(0,A0))]
							| None ->
								(deplacement (compile_compute_type expr.loc) A0 SP)
								@[Binop(Add,SP,SP,Oimm( -(compile_compute_type expr.loc) ))]
					)
				| Some _ -> 
					(compile_expr ~registre:A0 expr1 env) (** A0 contient l'addresse du pointeur **)
					@(
						match registre with
							| Some(x) -> [Move(x, A0)] (** x contient l'addresse de la case pointee **)
							| None -> [Sw(A0,Areg(0,SP)); Binop(Add,SP,SP,Oimm( -4 ))]
					)
				)
				
		| Uamp -> 
			(* @author : Julien Christophe *)
			(compile_expr ~calcul_adresse:true expr1 env)
		| Unot -> 
			(* @author : Julien Christophe *)
			(compile_expr ~registre:A0 expr1 env)
			@(
				match registre with
					| Some(x) -> [Binop(Eq, x, A0, Oreg(ZERO))]
					| None ->
						[	
							Binop(Eq, A0, A0, Oreg(ZERO));
							Sw(A0, Areg(0, SP)); 
							Binop(Add,SP,SP,Oimm( -(compile_compute_type expr1.loc) ))
						]
			)
					
		| Uminus -> 
			(* @author : Julien Christophe *)
			(compile_expr ~registre:A0 expr1 env)
			@(
				match registre with
					| Some(x) -> [Binop(Sub, x, ZERO, Oreg(A0))]
					| None ->
						[	
							Binop(Sub, A0, ZERO, Oreg(A0));
							Sw(A0, Areg(0, SP)); 
							Binop(Add,SP,SP,Oimm( -4 ))
						]
			)
		| Uplus -> 
			(* @author : Julien Christophe *)
			(compile_expr ~registre:A0 expr1 env)
			@(
				match registre with
					| Some(x) -> [Move(x, A0)]
					| None ->
						[	
							Sw(A0, Areg(0, SP)); 
							Binop(Add,SP,SP,Oimm( -4 ))
						]
			)
	)
  | Ebinop (binop, expr1, expr2) ->
  	(* @author : Julien Christophe *)
	let op = match binop with
		| Beq -> Eq
		| Bneq -> Ne
		| Blt -> Lt
		| Ble -> Le
		| Bgt -> Gt
		| Bge -> Ge
		| Badd -> Add
		| Bsub -> Sub
		| Bmul -> Mul
		| Bdiv -> Div
		| Bmod -> Rem
		| Band -> And
		| Bor -> Or
	in
	let normalisation = match binop with 
		| Beq | Bneq | Blt | Ble | Bgt | Bge | Badd | Bsub | Bmul | Bdiv | Bmod -> false
		| Band | Bor -> true
	in

	let e1 = compile_expr expr1 env in (** en haut de la pile **)
	let e2 = compile_expr ~registre:A0 expr2 env in (** directement dans A0 **)
	
	let pc = match binop, expr1.loc, expr2.loc with
				| (Badd, Tpointer t, Tint) | (Badd, Tpointer t, Tchar) 
				| (Bsub, Tpointer t, Tint) | (Bsub, Tpointer t, Tchar) ->
					[
						Binop(Mul, A0, A0, Oimm(compile_compute_type t))
					]
				| (_, _ , _) -> []
	in
	let code_normalisation =
		let n = num_unique () in
		let flag_norm = "norm_"^(string_of_int n) in
		let flag_norm_a0 = "norm_A0_"^(string_of_int n) in
		let flag_norm_a1 = "norm_A1_"^(string_of_int n) in
		let flag_end = "end_"^(string_of_int n) in
		[
			Mips.Label flag_norm;
			Mips.Label flag_norm_a0;
			Beqz(A0,flag_norm_a1);
			Binop(Add,A0,ZERO,Oimm(1));
			Mips.Label flag_norm_a1;
			Beqz(A1,flag_end);
			Binop(Add,A1,ZERO,Oimm(1));
			Mips.Label flag_end
		]
	in

 	e1
	@e2
	@pc
	@(match registre with
		| Some(x) -> 
			[
				Lw(A1,Areg(4,SP))
			]@
				(if normalisation then code_normalisation else [])
			@[
				Binop(op, x, A1, Oreg(A0));
				Binop(Add,SP,SP,Oimm(4))
			]
		| None -> 
			[
				Lw(A1,Areg(4,SP));
			]@
				(if normalisation then code_normalisation else [])
			@[
				Binop(op, A0, A1, Oreg(A0));
				Sw(A0,Areg(4,SP))
			]
				
	)

  | Ecall (ident, expr_list) ->
  	(* @author : Julien Christophe & Oliver De Cramer *)
		let taille_retour = compile_compute_type expr.loc in
		let arg_list = (Hashtbl.find decl_type_env_fun ident.node).decl_args in
		let taille_args = List.fold_left (fun n (_,a) -> n+a.decl_info_size) 0 arg_list in

		(	
			if taille_retour != 0 then 
				[
					Comment "Reservation de la place pour le retour"; 
					Binop(Add,SP,SP,Oimm(-taille_retour)); 
					Inline "\n"
				] 
			else []
		)
		@[Comment "Calcul des arguments..."]
		@(compile_expr_list expr_list arg_list env)
		@[
			Inline "\n"; 
			Comment "Juuuump !!!"; 
			Jal ident.node; 
			Inline "\n"
		]
		@(
			if taille_args != 0 then 
				[
					Comment "Liberation de la place pour les arguments"; 
					Binop(Add,SP,SP,Oimm(taille_args)); 
					Inline "\n"
				] 
			else []
		)
		@(match calcul_adresse with
		| Some _ ->
			(match registre with
			| Some(x) -> 
				[
					Binop(Add,x,SP,Oimm(taille_retour));
				]
			| None ->
				[
					Binop(Add,A0,SP,Oimm(taille_retour));
					Sw(A0,Areg(0,SP));
					Binop(Add,SP,SP,Oimm(-4))
				]
			)
		| None ->
			(match registre with
			| Some(x) -> 
				[
					Lw(x,Areg(4,SP)); 
					Binop(Add,SP,SP,Oimm(4))
				]
			| None -> []
			)	
		)

(* Calcul des offset pour les arguments de la fonction. 
 * Positione le offset en positif par rapport au fp 
 * Les variable locale qui sont place en negatif par rappor au fp
 *
 * @author : Julien Christophe 
 *)
and compile_local_args_decl_list var_decl_list env taille =
	match var_decl_list with
		|v::s ->
				let (v_type, ident) = v in
				let v_taille =  compile_compute_type v_type in
				Printf.printf "Arg : %s dec : %n taille : %n\n" ident.node (8+taille+v_taille)  taille;
				let taille = compile_local_var_decl v env taille (8+taille+v_taille) in 
				compile_local_args_decl_list s env taille
		
		|[] -> taille

(* Calcul des offset pour les variables locales d'un bloc 
 * Positione le offset en negatif par rapport au fp 
 *
 * @author : De Cramer Oliver 
 *)
and compile_local_var_decl (v_type, ident) env taille ?flag decalage =
	let v_taille =  compile_compute_type v_type in
	
	Hashtbl.add env ident.node
							{
								decl_info_size = v_taille;
								decl_info_offset = 
										match flag with 
											| None -> 
												decalage
											| Some _ -> 
												decalage-taille;		
							};
	(taille+v_taille)

(* Boucle sur toutes la liste des variables.
 * @author : Oliver De Cramer 
 *)
and compile_local_var_decl_list var_decl_list env taille ?flag decalage=
	match var_decl_list with
		|v::s -> (match flag with
			| None -> 
				let taille = compile_local_var_decl v env taille decalage in 
				compile_local_var_decl_list s env taille decalage
			| Some(flag) -> 
				let taille = compile_local_var_decl v env taille ~flag:flag decalage 
				in compile_local_var_decl_list s env taille ~flag:flag decalage
		)
		|[] -> taille

(* Compile la liste des instruction.
 * @author : De Cramer Oliver 
 *)
and compile_statement_list statement_list id env taille=
	match statement_list with
	| [] -> []
	| s::sl -> 
			(compile_statement s id env taille)
			@(compile_statement_list sl id env taille)

(* Compile une instruction 
 * @author : Christphe Julien
 *)
and compile_statement statement id env taille_vars =
	match statement.node with
  | Sskip -> []
  | Sexpr expr -> 
  	let taille = compile_compute_type expr.loc in
  	(compile_expr expr env)
  	@[Binop(Add, SP, SP, Oimm(taille))]
  	
  | Sif (expr, statement1, statement2) ->
  	(* @author : Christphe Julien *)
		let n = num_unique () in
		let flag_if = "if_"^(string_of_int n) in
		let flag_then = "then_"^(string_of_int n) in
		let flag_else = "else_"^(string_of_int n) in
		let flag_endif = "endif_"^(string_of_int n) in

		[Mips.Label flag_if]
		@compile_expr ~registre:A0 expr env (** expression **)
		@[Beqz(A0,flag_else)]
		@[Mips.Label flag_then]
		@compile_statement statement1 id env taille_vars (** statement **)
		@[B flag_endif]
		@[Mips.Label flag_else]
		@compile_statement statement2 id env taille_vars (** statement **)
		@[Mips.Label flag_endif]

  | Swhile (expr, statement) ->
  	(* @author : Christphe Julien *)
		let n = num_unique () in
		let flag_while = "while_"^(string_of_int n) in
		let flag_endwhile = "endwhile_"^(string_of_int n) in

		[Mips.Label flag_while]
		@compile_expr ~registre:A0 expr env (** expression **)
		@[Beqz(A0,flag_endwhile)]
		@compile_statement statement id env taille_vars (** statement **)
		@[B flag_while]
		@[Mips.Label flag_endwhile]

  | Sfor (statement_list1, expr, statement_list2, statement) ->
  	(* @author : Christphe Julien *)
		let n = num_unique () in
		let flag_init = "init_for_"^(string_of_int n) in (** statement_list1 **)
		let flag_cond = "cond_for_"^(string_of_int n) in (** expr **)
		let flag_block = "block_for_"^(string_of_int n) in (** statement **)
		let flag_gestion = "gestion_for_"^(string_of_int n) in (** statement_list2 **)
		let flag_end = "end_for_"^(string_of_int n) in

		[Mips.Label flag_init]
		@compile_statement_list statement_list1 id env taille_vars
		@[Mips.Label flag_cond]
		@compile_expr ~registre:A0 expr env (** expression **)
		@[Beqz(A0,flag_end)]
		@[Mips.Label flag_block]
		@compile_statement statement id env taille_vars (** statement **)
		@[Mips.Label flag_gestion]
		@compile_statement_list statement_list2 id env taille_vars
		@[B flag_cond]
		@[Mips.Label flag_end]

  | Sblock block -> 
  		(* @author : Oliver De Cramer *)
  		compile_bloc block id env taille_vars
  | Sreturn opt -> 	
  		(* @author : Oliver De Cramer *)
			(match opt with
				| Some expr -> 
  					let taille_retour = (Hashtbl.find decl_type_env_fun id.node).taille_retour in
  					let taille_args = 
  							List.fold_left 
  									(fun n (_,a) -> n + a.decl_info_size) 
  									0 
  									((Hashtbl.find decl_type_env_fun id.node).decl_args) 
  					in

  					
  					(compile_expr expr env) 
  					(** mettre retour dans le tas en -4(FP) **)
  					@
  					[
  						Binop(Add, A0, SP, Oimm(taille_retour));
  						Binop(Add, A1, FP, Oimm(8+taille_args+taille_retour));
  					]
  					@(deplacement taille_retour A0 A1)
  					@[
  						Inline "\n"; 
  						Comment ("Return :"); 
  						Binop(Add,SP,SP,Oimm(taille_retour)); 
  						Inline "\n"
  					]
  				| None -> []
  			)
  			@(recuperer_ra_fp ())
			@[
				Inline "\n"; 
				Comment "Juuuump !!!"; 
				Jr RA; 
				Inline "\n"
			]
(* Compile un block. Celui ci eut etre un bloc d'un if ou celui d'une fonction.
 * On considere que sp = fp et donc que dans le pile aucune valeur temporaraire est stocke
 *
 * @author : Oliver De Cramer 
 *)
and compile_bloc (var_decl_list, statement_list) id env taille =
	
	let nv_taille = compile_local_var_decl_list var_decl_list env taille ~flag:() 0 in
	let statement = (compile_statement_list statement_list id env nv_taille) in
	
	if nv_taille != 0 then
		[
			Comment "reservation de place pour var du bloc"; 
			Binop(Add,SP,SP,Oimm(taille-nv_taille)); 
			Inline "\n";
			Comment "Statements :"
		]
		@statement
		@[
			Comment "Fin statements"; 
			Inline "\n"; 
			Comment "recuperation de la place pour les var du bloc"; 
			Binop(Add,SP,SP,Oimm(nv_taille - taille))
		]
	else 
		[Comment "Statements :"]
		@statement
		@[Comment "Fin statements"]
	
(* Compile une fonction. 
 * Cree d'un un label pour la fonction le code pour enregistre au debut les registre important.
 * Puis compile le block et dernierement restitue les registre.
 *
 * @author : Oliver De Cramer 
 *)
let compile_function typ_retour id args bloc =

	let env = Hashtbl.create 1 in
	let taille = compile_local_args_decl_list (List.rev args) env 0 in
	Hashtbl.add 
		decl_type_env_fun 
		id.node 
		{ 
			decl_args = (Hashtbl.fold 
							(fun name args li -> (id.node, args)::li)
							env 
							[]
						); 
			taille_retour = (compile_compute_type typ_retour)
		};

	[	
		Inline "\n"; 
		Comment ("fonction " ^ id.node ^ " :"); 
		(Mips.Label id.node); 
		Inline "\n"
	]
	@(enregistrer_ra_fp ())
	@[Inline "\n"; Comment ("Bloc")]
	@(compile_bloc bloc id env 0)
	@[Comment "Fin Bloc"; Inline "\n"; Comment "Return de fin de fonction :"]
	@(recuperer_ra_fp ())
	@[Jr RA]
	@[Inline "\n"; Comment ("fin fonction " ^ id.node ^ ".")]

(* @author : Christophe Julien *)
let compile_file ast = 	
	
	(* On ajoute le putint dans l'environnement des fonctions *)
	Hashtbl.add 
		decl_type_env_fun 
		"putint"
		{ 
			decl_args = ["",
									{
											decl_info_size = 4;
											decl_info_offset = 8
									}]; 
			taille_retour = 0 
		};
		
	(* On ajoute le putchar dans l'environnement des fonctions *)
	Hashtbl.add 
		decl_type_env_fun 
		"putchar"
		{ 
			decl_args = ["",
									{
											decl_info_size = 4;
											decl_info_offset = 8
									}]; 
			taille_retour = 4
		};
	
	(* On ajoute sbrk dans l'environnement des fonctions *)
	Hashtbl.add 
		decl_type_env_fun 
		"sbrk"
		{ 
			decl_args = ["",
									{
											decl_info_size = 4;
											decl_info_offset = 8
									}]; 
			taille_retour = 4
		};

	(* La boucle pour la declaration des "globales" *)
	let rec loop ast instrs data =
		match ast with
		| [] -> (instrs,data)
		| a::s ->
			match a with
				| Dstruct(id, champs) -> 
					let info = compile_compute_info_struct champs in
					Hashtbl.add decl_type_env_struct id.node info;
					let (instrs,data) = loop s instrs data in (instrs,data)
				| Dunion(id, champs) -> 
					let info = compile_compute_info_union champs in
					Hashtbl.add decl_type_env_struct id.node info;
					let (instrs,data) = loop s instrs data in (instrs,data)
				| Dvars v -> 
					let (instrs,data) = loop s instrs data in (instrs,data@(compile_global_vars v))
				| Dfun (typ_retour, ident, args, bloc) ->
					let compiled_fun = compile_function typ_retour ident args bloc in
					let (instrs,data) = loop s instrs data in
					(compiled_fun@instrs, data)
	in
	let instr,data = loop ast [] [] in

	(* Le code Mips de la fonction putint *)
	let putint = 
		[
			Inline "\n"; 
			Comment ("fonction putint :"); 
			(Mips.Label "putint"); Inline "\n"
		]
		@(enregistrer_ra_fp ())
		 (** mettre 1 dans V0 **)
			(** mettre arg1 dans A0 **)
			(** syscall **)
		@
		[
			Li(V0,1); 
			Lw(A0,Areg(12,FP));
			Syscall
		]
		@[Comment "Return de fin de fonction :"]
		@(recuperer_ra_fp ())
		@[Jr RA]
		@[
			Inline "\n"; 
			Comment ("fin fonction putint.")
		]
	in
	
	(* Le code Mips de la fonction putchar *)
	let putchar =
		[
			Inline "\n"; 
			Comment ("fonction putchar :"); 
			(Mips.Label "putchar"); Inline "\n"
		]
		@(enregistrer_ra_fp ())
		 (** mettre 11 dans V0 **)
			(** mettre arg1 dans A0 **)
			(** syscall **)
		@
		[
			Li(V0,11); 
			Lw(A0,Areg(12,FP));
			Sw(A0,Areg(16,FP));
			Syscall
		]
		@[Comment "Return de fin de fonction :"]
		@(recuperer_ra_fp ())
		@[Jr RA]
		@[
			Inline "\n"; 
			Comment ("fin fonction putchar.")
		]
	in
	
	(* Le code Mips de la fonction sbrk *)
	let sbrk =
		[
			Inline "\n"; 
			Comment ("fonction sbrk :"); 
			(Mips.Label "sbrk"); Inline "\n"
		]
		@(enregistrer_ra_fp ())
		 (** mettre 9 dans V0 **)
			(** mettre arg1 dans A0 **)
			(** syscall **)
		@
		[
			Li(V0,9); 
			Lw(A0,Areg(12,FP));
			Sw(A0,Areg(16,FP));
			Syscall
		]
		@[Comment "Return de fin de fonction :"]
		@[Sw(V0,Areg(16,FP))]
		@(recuperer_ra_fp ())
		@[Jr RA]
		@[
			Inline "\n"; 
			Comment ("fin fonction sbrk.")
		]
	in
	
	(* Le code Mips de teoutes les fonction definie a la main *)
	let fonctions = putint@putchar@sbrk in
	
	let strings = List.fold_left (fun b (l,s) -> [Dlabel l; Dasciiz s; Dalign 3]@b) [] !strings in
	{
		text = Clist ([Comment "debut du programme, on saute au main"; Jal "main"; Li(V0,10); Syscall; Comment "fin du programme"]
			@instr
			@[Inline "\n"; Comment "FIN .text"; Inline "\n"; Inline "\n"; Comment "fonctions predefinies :"; Inline "\n"]
			@fonctions);
		data = data@strings;
	}
	
	
